﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;
using Snake.Cache;
using Snake.Core;
using Snake.Database;
using Snake.Extensions;
using Snake.Mapping;
using Snake.ORM.Reflection;
using Snake.ORM.SQLBuilder.SQLNode;
using Snake.SQL;
using Snake.SQL.SQLNode;
using Snake.SQLBuilder.SQLNode;
using Snake.Utils;
namespace Snake
{
    public class Session : ISession, IQueryProvider
    {
        #region 字段


        private readonly IDbConnection _connection;
        private readonly IEnumerable<EntityMap> _entities;
        private IDbTransaction _transaction;
        private bool _wasDisposed;

        #endregion

        public Session(IEnumerable<EntityMap> entities, IDbConnection connectionString)
        {
            _entities = entities;
            _connection = connectionString;
        }

        public void Open()
        {
            _connection.Open();
            _transaction = _connection.BeginTransaction();
        }


        #region---------------ADD--------------

        public void Save<T>(T entity)
        {
            var context = new DbContext(_entities, _connection) { EntityType = typeof(T) };
            var insert = new Insert<T>(context, entity);
            insert.Render(context.Writer);
            var iId = new SQLExecutor(context).ExecuteScalar<int>();
            if (context.EntityMap.Key.Property.PropertyType == typeof(int))
            {
                var handler = DynamicMethodCompiler.CreateSetPropertyHandler<T>(context.EntityMap.Key.Property);
                handler(entity, iId);
            }

        }


        #endregion

        #region ---------------Delete-----------
        public void Delete<T>(object primarykey) where T : class, new()
        {
            var context = new DbContext(_entities, _connection) { EntityType = typeof(T) };
            var delete = new Delete(context);
            delete.Where = new Where(
                new Equal
                {
                    Left = new PropertyAccess { Alias = delete.EntityMap.Table, PropertyMap = delete.EntityMap.Key },
                    Right = new Parameter(new ConstantProvider(context, primarykey))
                }
                );

            delete.Render(context.Writer);
            var executorSQL = context.Writer.GetResult();

            new SQLExecutor(context).InnerExecute<T>();
        }
        /// <summary>
        /// 删除一坨
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="primarykeys"></param>
        public void Delete<T>(IEnumerable<object> primarykeys)
        {
            var context = new DbContext(_entities, _connection) { EntityType = typeof(T) };
            var delete = new Delete(context);

            delete.Where = new Where(
                new IN(delete, primarykeys)
                );
            delete.Render(context.Writer);

            new SQLExecutor(context).InnerExecute<T>();

        }

        /// <summary>
        /// 传入的实体必须包含ID
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        public void Delete<T>(T entity)
        {
            var context = new DbContext(_entities, _connection) { EntityType = typeof(T) };
            var delete = new Delete(context);

            //拿属性
            var propertyInfo = typeof(T).GetProperty(context.EntityMap.Key.Column.Raw);

            //拿属性的get方法的委托
            var getHandler = DynamicMethodCompiler.CreatePropertyGetHandler<T>(propertyInfo);
            var _value = (int)getHandler(entity);
            if (_value <= 0)
                return;
            delete.Where = new Where(
              new Equal
              {
                  Left = new PropertyAccess { Alias = delete.EntityMap.Table, PropertyMap = delete.EntityMap.Key },
                  Right = new Parameter(new ConstantProvider(context, _value))
              }
              );

            delete.Render(context.Writer);

            new SQLExecutor(context).InnerExecute<T>();

        }
        #endregion

        #region ---------------Update-------------

        public void Update<T>(T entity)
        {
            var context = new DbContext(_entities, _connection) { EntityType = typeof(T) };
            var update = new Update<T>(context, entity);
            update.Render(context.Writer);
            new SQLExecutor(context).InnerExecute<T>();
        }

        #endregion

        #region---------------Query----------------

        public T Get<T>(object primarykey)
        {

            var entity = ContextPool.Instance.Get<T>(primarykey);
            if (entity != null)
            {
                return (T)entity;
            }

            var context = new DbContext(_entities, _connection) { EntityType = typeof(T) };
            var select = new Select(context);
            select.Where =
                new Where(
                    new Equal
                    {
                        Left = new PropertyAccess { Alias = select.Alias, PropertyMap = select.EntityMap.Key },
                        Right = new Parameter(new ConstantProvider(context, primarykey)),
                    });


            select.Render(context.Writer);

            entity = new SQLExecutor(context).GetEnumerableExecuteQuery<T>().FirstOrDefault();

            ContextPool.Instance.Add(entity, primarykey);

            return (T)entity;
        }
        public IQueryable<T> GetAll<T>()
        {
            return new Queryable<T>(this);
        }



        #endregion

        #region---------------ExecuteSQL-----------

        /*
         
         * ExecuteNonQuery: 执行不返回数据行的操作，并返回一个int类型的数据。
         * ExecuteReader: 执行查询，并返回一个 DataReader 对象。
         * ExecuteScalar: 执行查询，并返回查询结果集中第一行的第一列（object类型）。如果找不到结果集中第一行的第一列，则返回 null 引用。
         
         */
       
        #endregion

        //public object ConvertFromDbType(object value, Type type)
        //{
        //    return DbAdapter.ConvertFromDb(value, type);
        //}


        public void SaveOrUpdate<T>(T entity, string parentIdColumnName, object parentId) where T : class, new()
        {
            throw new NotImplementedException();
        }




        void ISession.SaveOrUpdate<T>(T entity)
        {
            SaveOrUpdate(entity);
        }

        public void SaveOrUpdate<T>(T entity)
        {
            throw new NotImplementedException();
        }



        public TRef LoadReference<TSource, TRef>(TSource obj, Expression<Func<TSource, TRef>> expr) where TRef : class
        {
            var context = new DbContext(_entities, _connection) { EntityType = typeof(TSource) };

            var prop = ExpressionHelper.GetPropertyInfo(expr);
            var reference = context.EntityMap.FindReferenceMap(prop);
            if (reference == null)
                throw new Exception(prop + " not found");

            var id = context.EntityMap.Key.Property.GetValue(obj, null);
            var select = new Select(context);

            var alias = context.NextAlias();
            select.JoinGroup.AddJoin(select.Alias, alias, reference).Join = SQLStrings.Join;

            select.Columns = new Columns(alias, reference.To);
            select.Where =
                new Where(
                    new Equal
                    {
                        Left = new PropertyAccess { Alias = select.Alias, PropertyMap = select.EntityMap.Key },
                        Right = new Parameter(new ConstantProvider(context, id)),
                    });


            select.Render(context.Writer);

            return new SQLExecutor(context).InnerExecute<TRef>().FirstOrDefault();
        }

        public IQueryable<TResult> LoadCollection<TSource, TResult>(TSource obj, Expression<Func<TSource, IEnumerable<TResult>>> expr) where TResult : class
        {
            throw new NotImplementedException();
        }

        public DbContext CreateContext(Type type, Expression expression, OperateEnum operate = OperateEnum.Select)
        {
            var context = new DbContext(_entities, _connection) { EntityType = type };

            var converter = new QueryConverter(context, operate);

            ISQLNode SQLNode;


            SQLNode = converter.Visit(expression);


            SQLNode.Render(context.Writer);

            return context;
        }



        public void Dispose()
        {
            if (_transaction != null)
            {
                _transaction.Commit();
            }
            if (_connection != null)
            {
                _connection.Close();
                _connection.Dispose();
            }
            _wasDisposed = true;
        }

        private void NotDisposed()
        {
            if (_wasDisposed)
            {
                throw new Exception("SessionDisposedException");
            }
        }


        /// <summary>
        /// 这个是ORM的一级缓存 基于会话的缓存
        /// </summary>
        /// <returns></returns>
        public static IDictionary getContextCache()
        {
            object dic = HttpContext.Current.Items[_contextCacheKey];
            if (dic == null)
            {
                dic = new Hashtable();
                HttpContext.Current.Items[_contextCacheKey] = dic;
            }
            return dic as IDictionary;
        }

        private const String _contextCacheKey = "__contextCacheDictionary";

        #region IQueryProvider
        TCollection IQueryProvider.Execute<TCollection>(Expression expression)
        {
            var type = typeof(TCollection).GetGenericArguments()[0];
            var context = CreateContext(type, expression);

            return new SQLExecutor(context).Execute<TCollection>();
        }

        IQueryable<T> IQueryProvider.CreateQuery<T>(Expression expression)
        {
            return new Queryable<T>(this, expression);
        }

        IQueryable IQueryProvider.CreateQuery(Expression expression)
        {
            throw new Exception("Use generic CreateQuery");
        }

        object IQueryProvider.Execute(Expression expression)
        {
            throw new Exception("Use generic Execute");
        }
        #endregion
    }
}
